﻿using System;
using VirtualTournaments.Business.Financial;
using VirtualTournaments.Business.Users;
using System.Collections.Generic;
using VirtualTournaments.Business.Tournaments;
using System.Linq;

namespace VirtualTournaments.Business.Bets
{
    /// <summary>
    /// Maneja la compra de apuestas y los pagos de las mismas, como tambien las comisiones
    /// </summary>
    public sealed class Agency
    {
        IBetTypeFactory _factory;

        /// <summary>
        /// porcentaje del dinero de las apuestas que se queda la casa
        /// </summary>
        private int _profit;

        public int Profit
        {
            get { return _profit; }
        }
       
        public Agency(int profit, IBetTypeFactory factory)
        {
            this._profit = profit;
            this._factory = factory;
            this.Account = new UnlimitedAccount();
            AvailableBetTypes = new List<BetType>();
        }

        public IAccount Account { get; private set; }

        public List<BetType> AvailableBetTypes { get;private set; }

		/// <summary>
		/// Apuesta una determinada cantidad de dinero a un resultado
		/// </summary>
		/// <param name="user"></param>
		/// <param name="resultToBet"></param>
		/// <param name="moneyToWager"></param>
		/// <returns></returns>
        public Bet Bet(VirtualTournaments.Business.Users.User user, BetTarget resultToBet, decimal moneyToWager)
        {
			BetTarget[] foundTargets = GetEqualsTargets(resultToBet);

			if (foundTargets.Count() != 1)
			{
				throw new BusinessException();
			}
			else {
				Bet bet = new Bet(user, moneyToWager, resultToBet.Odds);
				user.Account.transfer(this.Account, moneyToWager);
				resultToBet.AddBet(bet);
				return bet;
			}
        }

		private BetTarget[] GetEqualsTargets(BetTarget resultToBet)
		{
			var allBetTargets = new List<BetTarget>();
			this.AvailableBetTypes.ForEach(bettype => allBetTargets.AddRange(bettype.BetTargets.ToArray()));

			var betTargetsFound = from betTarget in allBetTargets
								   where betTarget.Equals(resultToBet)
								   select betTarget;
			return betTargetsFound.ToArray();
		}

        /// <summary>
        /// Sirve para avisar a la agencia que una competición finalizó. Se obtienen todos los betTypes con apuestas sobre esta competición y se obtienen las apuestas, que luego se pagan a sus respectivos usuarios.
        /// </summary>
        /// <param name="finishedCompetition"></param>
        public void NotifyCompetitionFinished(ICompetition finishedCompetition)
        {
            if (!finishedCompetition.HasFinished()) {
                throw new CompetitionNotFinishedException();
            }

            var competitionBetTypes = from betType in this.AvailableBetTypes.AsQueryable()
                                                     where betType.Competition.Equals(finishedCompetition)
                                                     select betType;

            foreach (var betType in competitionBetTypes)
            {
                foreach (var winningBet in betType.GetWinningBets())
                {
                    winningBet.Pay(this.Account, this.Profit);   
                }
            }
        }

        public void AddBetType(BetType betType)
        {
            this.AvailableBetTypes.Add(betType);
        }

        public void AddCompetition(ICompetition competition)
        {
            foreach (var betType in this._factory.createBetTypesFor(competition))
            {
                this.AddBetType(betType);
            }
        }

        public BetType GetBetTypeByCompetition(ICompetition competition)
        {
            var result = from betType in this.AvailableBetTypes
                         where betType.Competition.Equals(competition)
                         select betType;

            if (result.Count() > 0) {
                return result.ElementAt(0);
            }
            return null;
        }
    }
}
